<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>贪吃蛇 | hez2010</title>
  
  
  
  <!--link rel="stylesheet" href="//cdn.jsdelivr.net/highlight.js/9.10.0/styles/github-gist.min.css"-->
  
<link rel="stylesheet" href="//cdn.jsdelivr.net/highlight.js/9.10.0/styles/github-gist.min.css">

  
<link rel="stylesheet" href="/css/style.css">

<!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.1.1"></head>

<body>
<div class="Shell">
    <aside class='SideBar'>
    <section class='avatar' style="background-image: url()">
        <div class='av-pic' style="background-image: url(/assets/avatar.jpg)">
        </div>
    </section>
    <section class='menu'>
        <div>hez2010</div>
        
            <div>软件工程导论</div>
        
        <ul>
          
            <a href="/" class="Btn">
              <li>Home</li>
            </a>  
          
            <a href="/archives/" class="Btn">
              <li>Archive</li>
            </a>  
          
        </ul>
    </section>
    <section class="media">
        
            
                <a target="_blank" rel="noopener" href="https://github.com/hez2010">
                    <img src="/assets/github.svg" />
                </a>
            
        
    </section>
</aside>

    <div class="container">
        <div data-pager-shell>
            <div>
  <article class='ContentView'>
    <header class='PageTitle'>
        <h1>贪吃蛇</h1>
    </header>

    <section>
      <h1 id="C-编写贪吃蛇小游戏"><a href="#C-编写贪吃蛇小游戏" class="headerlink" title="C 编写贪吃蛇小游戏"></a>C 编写贪吃蛇小游戏</h1><p>项目代码见：<a target="_blank" rel="noopener" href="https://gitee.com/hez2010/snake">https://gitee.com/hez2010/snake</a></p>
<h2 id="会动的蛇"><a href="#会动的蛇" class="headerlink" title="会动的蛇"></a>会动的蛇</h2><h3 id="整体框架"><a href="#整体框架" class="headerlink" title="整体框架"></a>整体框架</h3><p>整体流程为：</p>
<ol>
<li>输出地图</li>
<li>如果游戏未结束<ol>
<li>等待用户输入并移动蛇</li>
<li>输出移动后的地图</li>
<li>重复上述过程</li>
</ol>
</li>
<li>否则输出 <code>Game Over!!</code></li>
</ol>
<a id="more"></a>

<p>因此，会动的蛇设计为以下几个函数：</p>
<ul>
<li><code>print_map</code> 输出地图</li>
<li><code>snake_mode</code> 移动蛇</li>
<li><code>game_over</code> 结束游戏</li>
</ul>
<p>用代码表示为：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs c">print_map();<br><span class="hljs-keyword">while</span> (!gameOver) &#123;<br>	input = getchar();<br>	<span class="hljs-keyword">switch</span> (input) &#123;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;A&#x27;</span>: <span class="hljs-comment">// 向左移动</span><br>		snake_move(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;D&#x27;</span>: <span class="hljs-comment">// 向右移动</span><br>		snake_move(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;W&#x27;</span>: <span class="hljs-comment">// 向上移动</span><br>		snake_move(<span class="hljs-number">-1</span>, <span class="hljs-number">0</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;S&#x27;</span>: <span class="hljs-comment">// 向下移动</span><br>		snake_move(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">default</span>:<br>		<span class="hljs-keyword">continue</span>;<br>	&#125;<br>	print_map();<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="输出地图"><a href="#输出地图" class="headerlink" title="输出地图"></a>输出地图</h3><p>首先利用打表的方式存储空地图：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// 空地图</span><br><span class="hljs-keyword">char</span> <span class="hljs-built_in">map</span>[<span class="hljs-number">12</span>][<span class="hljs-number">12</span>] = &#123;<br>    <span class="hljs-string">&quot;************&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;*          *&quot;</span>,<br>    <span class="hljs-string">&quot;************&quot;</span><br>&#125;;<br></code></pre></td></tr></table></figure>

<p>为了记录蛇的位置，可以利用数组分别保存蛇的行列坐标：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// 蛇身体的行坐标</span><br><span class="hljs-keyword">int</span> snakeX[SNAKE_MAX_LEN] = &#123; <span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span> &#125;;<br><br><span class="hljs-comment">// 蛇身体的列坐标</span><br><span class="hljs-keyword">int</span> snakeY[SNAKE_MAX_LEN] = &#123; <span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span> &#125;;<br></code></pre></td></tr></table></figure>

<p>这样一来，只需要判断坐标点是否记录在 <code>snakeX</code> 和 <code>snakeY</code> 中即可知道对应位置是不是蛇：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-keyword">if</span> (snakeX[k] == i &amp;&amp; snakeY[k] == j) &#123;<br>    ch = k == snakeLength - <span class="hljs-number">1</span> ? SNAKE_HEAD : SNAKE_BODY;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>然后根据位置输出对应的字符即可。</p>
<h3 id="移动蛇"><a href="#移动蛇" class="headerlink" title="移动蛇"></a>移动蛇</h3><p>移动蛇的步骤为：</p>
<ol>
<li>计算蛇头的新位置 <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-keyword">int</span> i, x = snakeX[snakeLength - <span class="hljs-number">1</span>] + dx, y = snakeY[snakeLength - <span class="hljs-number">1</span>] + dy;<br></code></pre></td></tr></table></figure></li>
<li>判断是否撞到墙壁，若撞到则游戏结束 <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">10</span> || x &lt; <span class="hljs-number">1</span> || y &gt; <span class="hljs-number">10</span> || y &lt; <span class="hljs-number">1</span>) &#123;<br>    game_over();<br>    <span class="hljs-keyword">return</span>;<br>&#125;<br></code></pre></td></tr></table></figure></li>
<li>判断是否撞到自身，若撞到则游戏结束 <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt; snakeLength; i++) &#123;<br>    <span class="hljs-keyword">if</span> (snakeX[i] == x &amp;&amp; snakeY[i] == y) &#123;<br>        game_over();<br>        <span class="hljs-keyword">return</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
<li>更新蛇的位置 <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; snakeLength - <span class="hljs-number">1</span>; i++) &#123;<br>    snakeX[i] = snakeX[i + <span class="hljs-number">1</span>];<br>    snakeY[i] = snakeY[i + <span class="hljs-number">1</span>];<br>&#125;<br>snakeX[snakeLength - <span class="hljs-number">1</span>] = x;<br>snakeY[snakeLength - <span class="hljs-number">1</span>] = y;<br></code></pre></td></tr></table></figure>

</li>
</ol>
<h3 id="结束游戏"><a href="#结束游戏" class="headerlink" title="结束游戏"></a>结束游戏</h3><p>程序中利用一个全局变量 <code>gameOver</code> 指示游戏是否结束，因此结束游戏只需要将这个变量设置为 <code>1</code> 即可。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">game_over</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;<br>    gameOver = <span class="hljs-number">1</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="会吃的蛇"><a href="#会吃的蛇" class="headerlink" title="会吃的蛇"></a>会吃的蛇</h2><h3 id="整体框架-1"><a href="#整体框架-1" class="headerlink" title="整体框架"></a>整体框架</h3><p>会吃的蛇相比会动的蛇多了食物的生成和吃的行为，流程如下：</p>
<ol>
<li>生成食物</li>
<li>输出地图</li>
<li>如果游戏未结束<ol>
<li>等待用户输入并移动蛇</li>
<li>如果吃到食物则更新蛇的长度并生成新食物</li>
<li>输出移动后的地图</li>
<li>重复上述过程</li>
</ol>
</li>
<li>否则输出 <code>Game Over!!</code></li>
</ol>
<p>因此，会吃的蛇设计为以下几个函数：</p>
<ul>
<li><code>print_map</code> 输出地图</li>
<li><code>snake_mode</code> 移动蛇</li>
<li><code>game_over</code> 结束游戏</li>
<li><code>generate_food</code> 生成食物</li>
</ul>
<p>用代码表示为：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs c">generate_food();<br>print_map();<br><span class="hljs-keyword">while</span> (!gameOver) &#123;<br>	input = getchar();<br>	<span class="hljs-keyword">switch</span> (input) &#123;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;A&#x27;</span>: <span class="hljs-comment">// 向左移动</span><br>		snake_move(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;D&#x27;</span>: <span class="hljs-comment">// 向右移动</span><br>		snake_move(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;W&#x27;</span>: <span class="hljs-comment">// 向上移动</span><br>		snake_move(<span class="hljs-number">-1</span>, <span class="hljs-number">0</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;S&#x27;</span>: <span class="hljs-comment">// 向下移动</span><br>		snake_move(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br>		<span class="hljs-keyword">break</span>;<br>	<span class="hljs-keyword">default</span>:<br>		<span class="hljs-keyword">continue</span>;<br>	&#125;<br>	print_map();<br>&#125;<br></code></pre></td></tr></table></figure>

<p>由于其他部分和会动的蛇基本一致，接下来只对新增的部分进行说明。</p>
<h3 id="生成食物"><a href="#生成食物" class="headerlink" title="生成食物"></a>生成食物</h3><p>生成的食物需要确保食物出现在空白的位置，这个位置不能和蛇所在的位置重叠，因此只需要不断获取随机数直到找到满足要求的位置即可。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">generate_food</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;<br>	<span class="hljs-keyword">int</span> isSnakeBody, i;<br>	<span class="hljs-keyword">do</span> &#123;<br>		isSnakeBody = <span class="hljs-number">0</span>;<br>		foodX = rand() % <span class="hljs-number">10</span> + <span class="hljs-number">1</span>;<br>		foodY = rand() % <span class="hljs-number">10</span> + <span class="hljs-number">1</span>;<br>		<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; snakeLength; i++) &#123;<br>			<span class="hljs-keyword">if</span> (snakeX[i] == foodX &amp;&amp; snakeY[i] == foodY) &#123;<br>				isSnakeBody = <span class="hljs-number">1</span>;<br>				<span class="hljs-keyword">break</span>;<br>			&#125;<br>		&#125;<br>	&#125; <span class="hljs-keyword">while</span> (isSnakeBody);<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="移动蛇-1"><a href="#移动蛇-1" class="headerlink" title="移动蛇"></a>移动蛇</h3><p>这一部分相比会动的蛇，多了判断是否吃到食物的过程，如果吃到了食物需要加长蛇的身体并重新生成食物。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// 判断是否吃到食物</span><br><span class="hljs-keyword">if</span> (x == foodX &amp;&amp; y == foodY) &#123;<br>    <span class="hljs-comment">// 若蛇超过最长长度则游戏结束</span><br>    <span class="hljs-keyword">if</span> (snakeLength + <span class="hljs-number">1</span> == SNAKE_MAX_LEN) &#123;<br>        game_over();<br>        <span class="hljs-keyword">return</span>;<br>    &#125;<br>    snakeX[snakeLength] = x;<br>    snakeY[snakeLength] = y;<br>    snakeLength++;<br>    generate_food();<br>    <span class="hljs-keyword">return</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="输出地图-1"><a href="#输出地图-1" class="headerlink" title="输出地图"></a>输出地图</h3><p>相比会动的蛇多了判断是否为食物的逻辑，其余没有改变。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-keyword">if</span> (i == foodX &amp;&amp; j == foodY) &#123;<br>    ch = SNAKE_FOOD;<br>&#125;<br></code></pre></td></tr></table></figure>


      

    </section>
    
      <section class='ArticleMeta'>
          <div>
            发布于&nbsp;
            <time datetime="2020-12-30T15:36:30.475Z" itemprop="datePublished">
              2020-12-30
            </time>
          </div>
          
      </section>
    
    
</article>

  
</div>

            <footer>
    <div>© 2021 - Steven He </div>
    <div>
        <span>
            Powered by <a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a>
        </span>
        ,
        <span>
            Theme - <a target="_blank" rel="noopener" href="https://github.com/nameoverflow/hexo-theme-icalm">Icalm</a>
        </span>
    </div>
</footer>

        </div>
    </div>
</div>

<script src="/js/pager/dist/singlepager.js"></script>

<script>
var sp = new Pager('data-pager-shell')

</script>
</body>
</html>